 /* ====================================================================
  *
  * The Apache Software License, Version 1.1
  *
  * Copyright (c) 1999-2001 The Apache Software Foundation. All rights
  * reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  *
  * 1. Redistributions of source code must retain the above copyright
  * notice, this list of conditions and the following disclaimer.
  *
  * 2. Redistributions in binary form must reproduce the above copyright
  * notice, this list of conditions and the following disclaimer in
  * the documentation and/or other materials provided with the
  * distribution.
  *
  * 3. The end-user documentation included with the redistribution, if
  * any, must include the following acknowlegement:
  * "This product includes software developed by the
  * Apache Software Foundation (http://www.apache.org/)."
  * Alternately, this acknowlegement may appear in the software itself,
  * if and wherever such third-party acknowlegements normally appear.
  *
  * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
  * Foundation" must not be used to endorse or promote products derived
  * from this software without prior written permission. For written
  * permission, please contact apache@apache.org.
  *
  * 5. Products derived from this software may not be called "Apache"
  * nor may "Apache" appear in their names without prior written
  * permission of the Apache Group.
  *
  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  * ====================================================================
  *
  * This software consists of voluntary contributions made by many
  * individuals on behalf of the Apache Software Foundation. For more
  * information on the Apache Software Foundation, please see
  * <http://www.apache.org/>.
  *
  * Portion of this source code are:
  *
  * Copyright (c) 2003, 2004 IBM Corp.
  * All rights reserved.
  *
  * Contributors:
  * Remy Maucherat
  * Craig R. McClanahan
  * <a HREF="mailto:nicolaken@supereva.it">Nicola Ken Barozzi</a> Aisa
  * <a HREF="mailto:stefano@apache.org">Stefano Mazzocchi</a>
  * Konrad Kolosowski, IBM - friendly 404 message, no exception printed
  */


 package org.eclipse.tomcat.internal;


 import java.io.*;
 import java.util.*;

 import javax.servlet.*;
 import javax.servlet.http.*;

 import org.apache.catalina.*;
 import org.apache.catalina.util.*;
 import org.apache.catalina.valves.*;
 import org.eclipse.core.runtime.Platform;


 /**
  * <p>Implementation of a Valve that outputs HTML error pages.</p>
  *
  * <p>This Valve should be attached at the Host level, although it will work
  * if attached to a Context.</p>
  *
  * <p>HTML code from the Cocoon 2 project.</p>
  */

 public class EclipseErrorReportValve
     extends ValveBase {


     // ----------------------------------------------------- Instance Variables


     /**
      * The debugging detail level for this component.
      */
     private int debug = 0;


     /**
      * The descriptive information related to this implementation.
      */
     private static final String info =
     "org.eclipse.tomcat.internal.EclipseErrorReportValve"; //$NON-NLS-1$


     /**
      * The StringManager for this package.
      */
     protected static StringManager sm =
         StringManager.getManager(Constants.Package);


     // ------------------------------------------------------------- Properties


     /**
      * Return descriptive information about this Valve implementation.
      */
     public String getInfo() {

         return (info);

     }


     // --------------------------------------------------------- Public Methods


     /**
      * Invoke the next Valve in the sequence. When the invoke returns, check
      * the response state, and output an error report is necessary.
      *
      * @param request The servlet request to be processed
      * @param response The servlet response to be created
      * @param context The valve context used to invoke the next valve
      * in the current processing pipeline
      *
      * @exception IOException if an input/output error occurs
      * @exception ServletException if a servlet error occurs
      */
     public void invoke(Request request, Response response,
                        ValveContext context)
         throws IOException, ServletException {

         // Perform the request
 context.invokeNext(request, response);

         ServletRequest sreq = (ServletRequest) request;
         Throwable throwable =
             (Throwable ) sreq.getAttribute(Globals.EXCEPTION_ATTR);

         ServletResponse sresp = (ServletResponse) response;
         if (sresp.isCommitted()) {
             return;
         }

         if (throwable != null) {

             // The response is an error
 response.setError();

             // Reset the response (if possible)
 try {
                 sresp.reset();
             } catch (IllegalStateException e) {
                 ;
             }

             ServletResponse sresponse = (ServletResponse) response;
             if (sresponse instanceof HttpServletResponse)
                 ((HttpServletResponse) sresponse).sendError
                     (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

         }

         response.setSuspended(false);

         try {
             report(request, response, throwable);
         } catch (Throwable tt) {
             // tt.printStackTrace();
 }

     }


     /**
      * Return a String rendering of this object.
      */
     public String toString() {

         StringBuffer sb = new StringBuffer ("EclipseErrorReportValve["); //$NON-NLS-1$
 sb.append(container.getName());
         sb.append("]"); //$NON-NLS-1$
 return (sb.toString());

     }


     // ------------------------------------------------------ Protected Methods


     /**
      * Prints out an error report.
      *
      * @param request The request being processed
      * @param response The response being generated
      * @param exception The exception that occurred (which possibly wraps
      * a root cause exception
      */
     protected void report(Request request, Response response,
                           Throwable throwable)
         throws IOException {

         // Do nothing on non-HTTP responses
 if (!(response instanceof HttpResponse))
             return;
         HttpResponse hresponse = (HttpResponse) response;
         if (!(response instanceof HttpServletResponse))
             return;
         HttpServletResponse hres = (HttpServletResponse) response;
         int statusCode = hresponse.getStatus();
         String message = RequestUtil.filter(hresponse.getMessage());
         if (message == null)
             message = ""; //$NON-NLS-1$

         // Do nothing on a 1xx, 2xx and 3xx status
 if (statusCode < 400)
             return;

         // FIXME: Reset part of the request
 /*
         try {
             if (hresponse.isError())
                 hresponse.reset(statusCode, message);
         } catch (IllegalStateException e) {
             ;
         }
 */

         Throwable rootCause = null;

         if (throwable != null) {

             if (throwable instanceof ServletException)
                 rootCause = ((ServletException) throwable).getRootCause();

         }

         // Do nothing if there is no report for the specified status code
 String report = null;
         try {
             report = sm.getString("http." + statusCode, message); //$NON-NLS-1$
 } catch (Throwable t) {
             ;
         }
         if (report == null)
             return;

         StringBuffer sb = new StringBuffer ();

         sb.append("<html><head><title>"); //$NON-NLS-1$
 sb.append(sm.getString("errorReportValve.errorReport")); //$NON-NLS-1$
 sb.append("</title>"); //$NON-NLS-1$
 if (statusCode == 404) {
             sb.append("</head><body>"); //$NON-NLS-1$
 sb.append(TomcatResources.noDocument);
         } else {
         sb.append("<STYLE><!--"); //$NON-NLS-1$
 sb.append("H1{font-family : sans-serif,Arial,Tahoma;color : white;background-color : black;} "); //$NON-NLS-1$
 sb.append("H3{font-family : sans-serif,Arial,Tahoma;color : white;background-color : black;} "); //$NON-NLS-1$
 sb.append("BODY{font-family : sans-serif,Arial,Tahoma;color : black;background-color : white;} "); //$NON-NLS-1$
 sb.append("B{color : white;background-color : black;} "); //$NON-NLS-1$
 sb.append("HR{color : black;} "); //$NON-NLS-1$
 sb.append("--></STYLE> "); //$NON-NLS-1$
 sb.append("</head><body>"); //$NON-NLS-1$
 sb.append("<h1>"); //$NON-NLS-1$
 sb.append(sm.getString("errorReportValve.statusHeader", //$NON-NLS-1$
 "" + statusCode, message)).append("</h1>"); //$NON-NLS-1$ //$NON-NLS-2$
 sb.append("<HR size=\"1\" noshade>"); //$NON-NLS-1$
 sb.append("<p><b>type</b> "); //$NON-NLS-1$
 if (throwable != null) {
             sb.append(sm.getString("errorReportValve.exceptionReport")); //$NON-NLS-1$
 } else {
             sb.append(sm.getString("errorReportValve.statusReport")); //$NON-NLS-1$
 }
         sb.append("</p>"); //$NON-NLS-1$
 sb.append("<p><b>"); //$NON-NLS-1$
 sb.append(sm.getString("errorReportValve.message")); //$NON-NLS-1$
 sb.append("</b> <u>"); //$NON-NLS-1$
 sb.append(message).append("</u></p>"); //$NON-NLS-1$
 sb.append("<p><b>"); //$NON-NLS-1$
 sb.append(sm.getString("errorReportValve.description")); //$NON-NLS-1$
 sb.append("</b> <u>"); //$NON-NLS-1$
 sb.append(report);
         sb.append("</u></p>"); //$NON-NLS-1$

         if (throwable != null) {
             boolean selfHostingMode = false;
             String [] args = Platform.getCommandLineArgs();
             for (int i = 0; i < args.length; i++) {
                 if ("-pdelaunch".equals(args[i])) { //$NON-NLS-1$
 selfHostingMode = true;
                     break;
                 }
             }
             if (selfHostingMode) {
             StringWriter stackTrace = new StringWriter();
             throwable.printStackTrace(new PrintWriter(stackTrace));
             sb.append("<p><b>"); //$NON-NLS-1$
 sb.append(sm.getString("errorReportValve.exception")); //$NON-NLS-1$
 sb.append("</b> <pre>"); //$NON-NLS-1$
 sb.append(RequestUtil.filter(stackTrace.toString()));
             sb.append("</pre></p>"); //$NON-NLS-1$
 if (rootCause != null) {
                 stackTrace = new StringWriter();
                 rootCause.printStackTrace(new PrintWriter(stackTrace));
                 sb.append("<p><b>"); //$NON-NLS-1$
 sb.append(sm.getString("errorReportValve.rootCause")); //$NON-NLS-1$
 sb.append("</b> <pre>"); //$NON-NLS-1$
 sb.append(RequestUtil.filter(stackTrace.toString()));
                 sb.append("</pre></p>"); //$NON-NLS-1$
 }
             }
         }

         }
         sb.append("</body></html>"); //$NON-NLS-1$

         try {

             Writer writer = response.getReporter();

             if (writer != null) {

                 Locale locale = Locale.getDefault();

                 try {
                     hres.setContentType("text/html"); //$NON-NLS-1$
 hres.setLocale(locale);
                 } catch (Throwable t) {
                     if (debug >= 1)
                         log("status.setContentType", t); //$NON-NLS-1$
 }

                 // If writer is null, it's an indication that the response has
 // been hard committed already, which should never happen
 writer.write(sb.toString());
                 writer.flush();

             }

         } catch (IOException e) {
             ;
         } catch (IllegalStateException e) {
             ;
         }

     }


     /**
      * Log a message on the Logger associated with our Container (if any).
      *
      * @param message Message to be logged
      */
     protected void log(String message) {

         Logger logger = container.getLogger();
         if (logger != null)
             logger.log(this.toString() + ": " + message); //$NON-NLS-1$
 else
             System.out.println(this.toString() + ": " + message); //$NON-NLS-1$

     }


     /**
      * Log a message on the Logger associated with our Container (if any).
      *
      * @param message Message to be logged
      * @param throwable Associated exception
      */
     protected void log(String message, Throwable throwable) {

         Logger logger = container.getLogger();
         if (logger != null)
             logger.log(this.toString() + ": " + message, throwable); //$NON-NLS-1$
 else {
             System.out.println(this.toString() + ": " + message); //$NON-NLS-1$
 throwable.printStackTrace(System.out);
         }

     }


 }

